| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- 'use client';
- import { useEffect, useRef, useState } from 'react';
- import { DonationAlertData, DonationAlertConfig } from '@/types/donation';
- import './style.scss';
- type Props = {
- alert: DonationAlertData;
- config: DonationAlertConfig;
- isAudioOnly: boolean;
- isVideoOnly: boolean;
- onComplete: () => void;
- };
- /**
- * config.message 템플릿의 {이름}, {금액}을 실제 값으로 치환하여 JSX 반환.
- * 각 변수에 sender-name / sender-amount 클래스를 적용해 폰트 스타일 유지.
- */
- function resolveTemplate(template: string, sendName: string, amount: number): React.ReactNode {
- const formattedAmount = `${amount.toLocaleString()}원`;
- const parts = template.split(/(\{이름\}|\{금액\})/g);
- return parts.map((part, i) => {
- if (part === '{이름}') {
- return <span key={i} className="donation-alert__name">{sendName}</span>;
- }
- if (part === '{금액}') {
- return <span key={i} className="donation-alert__amount">{formattedAmount}</span>;
- }
- return <span key={i} className="donation-alert__template-text">{part}</span>;
- });
- }
- export default function View({ alert, config, isAudioOnly, isVideoOnly, onComplete }: Props)
- {
- const [phase, setPhase] = useState<'delay'|'enter'|'show'|'exit'>('delay');
- const audioRef = useRef<HTMLAudioElement|null>(null);
- const timerRef = useRef<NodeJS.Timeout|null>(null);
- useEffect(() => {
- const delayMs = (config.playDelaySec || 0) * 1000;
- timerRef.current = setTimeout(() => {
- setPhase('enter');
- // 사운드 재생
- if (config.enableSound && config.soundUrl && !isVideoOnly) {
- const audio = new Audio(config.soundUrl);
- audioRef.current = audio;
- audio.play().catch(() => {});
- }
- // enter → show (Animate.css 기본 1초)
- setTimeout(() => {
- setPhase('show');
- timerRef.current = setTimeout(() => {
- setPhase('exit');
- setTimeout(() => {
- onComplete();
- }, 500);
- }, (config.displayDurationSec || 10) * 1000);
- }, 1000);
- }, delayMs);
- return () => {
- if (timerRef.current) {
- clearTimeout(timerRef.current);
- }
- if (audioRef.current) {
- audioRef.current.pause();
- audioRef.current = null;
- }
- };
- }, []);
- if (phase === 'delay') {
- return null;
- }
- // Animate.css 클래스
- const popupAnim = config.popupEffect || 'fadeIn';
- const textAnim = config.textEffect ? `animate__animated animate__infinite animate__${config.textEffect}` : '';
- const containerClass = phase === 'enter'
- ? `donation-alert animate__animated animate__${popupAnim}`
- : phase === 'exit'
- ? 'donation-alert animate__animated animate__fadeOut'
- : 'donation-alert alert-show';
- // CSS custom properties
- const cssVars = {
- '--nickname-font-family': config.nicknameFontFamily || 'inherit',
- '--nickname-font-size': `${config.nicknameFontSize}px`,
- '--nickname-font-color': config.nicknameFontColor || '#FFD700',
- '--amount-font-family': config.amountFontFamily || 'inherit',
- '--amount-font-size': `${config.amountFontSize}px`,
- '--amount-font-color': config.amountFontColor || '#FF6B35',
- '--message-font-family': config.messageFontFamily || 'inherit',
- '--message-font-size': `${config.messageFontSize}px`,
- '--message-font-color': config.messageFontColor || '#FFFFFF',
- '--template-font-family': config.templateFontFamily || 'inherit',
- '--template-font-size': `${config.templateFontSize}px`,
- '--template-font-color': config.templateFontColor || '#FFFFFF',
- } as React.CSSProperties;
- return (
- <div className={containerClass} style={cssVars}>
- {/* 이미지 */}
- {!isAudioOnly && config.enableImage && config.imageUrl && (
- <div className="donation-alert__image">
- <img src={config.imageUrl} alt="donation" />
- </div>
- )}
- {/* 후원 정보 */}
- {!isAudioOnly && (
- <div className="donation-alert__content">
- {/* 알림 메시지 (config.message 템플릿 치환) */}
- <div className={`donation-alert__sender ${textAnim}`}>
- {config.message
- ? resolveTemplate(config.message, alert.sendName, alert.amount)
- : (<>
- <span className="donation-alert__name">{alert.sendName}</span>
- <span className="donation-alert__amount">{alert.amount.toLocaleString()}원</span>
- </>)
- }
- </div>
- {/* 후원자 전달 내용 */}
- {alert.message && (
- <div className={`donation-alert__message ${textAnim}`}>{alert.message}</div>
- )}
- </div>
- )}
- </div>
- );
- }
|